package com.deviceplug;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbInterface;
import android.text.TextUtils;
import android.util.Log;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class CDeviceFilter {
    private static final String TAG = "CDeviceFilter";

    public final int mVendorId;

    public final int mProductId;

    public final int mClass;

    public final int mSubclass;

    public final int mProtocol;

    public final String mManufacturerName;

    public final String mProductName;

    public final String mSerialNumber;

    public CDeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, String manufacturer, String product, String serialnum) {
        this.mVendorId = vid;
        this.mProductId = pid;
        this.mClass = clasz;
        this.mSubclass = subclass;
        this.mProtocol = protocol;
        this.mManufacturerName = manufacturer;
        this.mProductName = product;
        this.mSerialNumber = serialnum;
    }

    public CDeviceFilter(UsbDevice device) {
        this.mVendorId = device.getVendorId();
        this.mProductId = device.getProductId();
        this.mClass = device.getDeviceClass();
        this.mSubclass = device.getDeviceSubclass();
        this.mProtocol = device.getDeviceProtocol();
        this.mManufacturerName = null;
        this.mProductName = null;
        this.mSerialNumber = null;
    }

    public static List<CDeviceFilter> getDeviceFilters(Context context, int deviceFilterXmlId) {
        XmlResourceParser xmlResourceParser = context.getResources().getXml(deviceFilterXmlId);
        List<CDeviceFilter> deviceFilters = new ArrayList<>();
        try {
            int eventType = xmlResourceParser.getEventType();
            while (eventType != 1) {
                if (eventType == 2) {
                    CDeviceFilter deviceFilter = read(context, (XmlPullParser)xmlResourceParser);
                    if (deviceFilter != null)
                        deviceFilters.add(deviceFilter);
                }
                eventType = xmlResourceParser.next();
            }
        } catch (XmlPullParserException e) {
            Log.d("CDeviceFilter", "XmlPullParserException", (Throwable)e);
        } catch (IOException e) {
            Log.d("CDeviceFilter", "IOException", e);
        }
        return Collections.unmodifiableList(deviceFilters);
    }

    private static final int getAttributeInteger(Context context, XmlPullParser parser, String namespace, String name, int defaultValue) {
        int result = defaultValue;
        try {
            String v = parser.getAttributeValue(namespace, name);
            if (!TextUtils.isEmpty(v) && v.startsWith("@")) {
                String r = v.substring(1);
                int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
                if (resId > 0)
                    result = context.getResources().getInteger(resId);
            } else {
                int radix = 10;
                if (v != null && v.length() > 2 && v.charAt(0) == '0' && (v
                        .charAt(1) == 'x' || v.charAt(1) == 'X')) {
                    radix = 16;
                    v = v.substring(2);
                }
                result = Integer.parseInt(v, radix);
            }
        } catch (Resources.NotFoundException e) {
            result = defaultValue;
        } catch (NumberFormatException e) {
            result = defaultValue;
        } catch (NullPointerException e) {
            result = defaultValue;
        }
        return result;
    }

    private static final String getAttributeString(Context context, XmlPullParser parser, String namespace, String name, String defaultValue) {
        String result = defaultValue;
        try {
            result = parser.getAttributeValue(namespace, name);
            if (result == null)
                result = defaultValue;
            if (!TextUtils.isEmpty(result) && result.startsWith("@")) {
                String r = result.substring(1);
                int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
                if (resId > 0)
                    result = context.getResources().getString(resId);
            }
        } catch (Resources.NotFoundException e) {
            result = defaultValue;
        } catch (NumberFormatException e) {
            result = defaultValue;
        } catch (NullPointerException e) {
            result = defaultValue;
        }
        return result;
    }

    public static CDeviceFilter read(Context context, XmlPullParser parser) throws XmlPullParserException, IOException {
        int vendorId = -1;
        int productId = -1;
        int deviceClass = -1;
        int deviceSubclass = -1;
        int deviceProtocol = -1;
        String manufacturerName = null;
        String productName = null;
        String serialNumber = null;
        boolean hasValue = false;
        int eventType = parser.getEventType();
        while (eventType != 1) {
            String tag = parser.getName();
            if (!TextUtils.isEmpty(tag) && tag.equalsIgnoreCase("usb-device"))
                if (eventType == 2) {
                    hasValue = true;
                    vendorId = getAttributeInteger(context, parser, null, "vendor-id", -1);
                    if (vendorId == -1) {
                        vendorId = getAttributeInteger(context, parser, null, "vendorId", -1);
                        if (vendorId == -1)
                            vendorId = getAttributeInteger(context, parser, null, "venderId", -1);
                    }
                    productId = getAttributeInteger(context, parser, null, "product-id", -1);
                    if (productId == -1)
                        productId = getAttributeInteger(context, parser, null, "productId", -1);
                    deviceClass = getAttributeInteger(context, parser, null, "class", -1);
                    deviceSubclass = getAttributeInteger(context, parser, null, "subclass", -1);
                    deviceProtocol = getAttributeInteger(context, parser, null, "protocol", -1);
                    manufacturerName = getAttributeString(context, parser, null, "manufacturer-name", null);
                    if (TextUtils.isEmpty(manufacturerName))
                        manufacturerName = getAttributeString(context, parser, null, "manufacture", null);
                    productName = getAttributeString(context, parser, null, "product-name", null);
                    if (TextUtils.isEmpty(productName))
                        productName = getAttributeString(context, parser, null, "product", null);
                    serialNumber = getAttributeString(context, parser, null, "serial-number", null);
                    if (TextUtils.isEmpty(serialNumber))
                        serialNumber = getAttributeString(context, parser, null, "serial", null);
                } else if (eventType == 3 &&
                        hasValue) {
                    return new CDeviceFilter(vendorId, productId, deviceClass, deviceSubclass, deviceProtocol, manufacturerName, productName, serialNumber);
                }
            eventType = parser.next();
        }
        return null;
    }

    private boolean matches(int clasz, int subclass, int protocol) {
        return ((this.mClass == -1 || clasz == this.mClass) && (this.mSubclass == -1 || subclass == this.mSubclass) && (this.mProtocol == -1 || protocol == this.mProtocol));
    }

    public boolean matches(UsbDevice device) {
        if (this.mVendorId != -1 && device.getVendorId() != this.mVendorId)
            return false;
        if (this.mProductId != -1 && device.getProductId() != this.mProductId)
            return false;
        if (matches(device.getDeviceClass(), device.getDeviceSubclass(), device
                .getDeviceProtocol()))
            return true;
        int count = device.getInterfaceCount();
        for (int i = 0; i < count; i++) {
            UsbInterface intf = device.getInterface(i);
            if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), intf
                    .getInterfaceProtocol()))
                return true;
        }
        return false;
    }

    public boolean matches(CDeviceFilter f) {
        if (this.mVendorId != -1 && f.mVendorId != this.mVendorId)
            return false;
        if (this.mProductId != -1 && f.mProductId != this.mProductId)
            return false;
        if (f.mManufacturerName != null && this.mManufacturerName == null)
            return false;
        if (f.mProductName != null && this.mProductName == null)
            return false;
        if (f.mSerialNumber != null && this.mSerialNumber == null)
            return false;
        if (this.mManufacturerName != null && f.mManufacturerName != null &&
                !this.mManufacturerName.equals(f.mManufacturerName))
            return false;
        if (this.mProductName != null && f.mProductName != null &&
                !this.mProductName.equals(f.mProductName))
            return false;
        if (this.mSerialNumber != null && f.mSerialNumber != null &&
                !this.mSerialNumber.equals(f.mSerialNumber))
            return false;
        return matches(f.mClass, f.mSubclass, f.mProtocol);
    }

    public boolean equals(Object obj) {
        if (this.mVendorId == -1 || this.mProductId == -1 || this.mClass == -1 || this.mSubclass == -1 || this.mProtocol == -1)
            return false;
        if (obj instanceof CDeviceFilter) {
            CDeviceFilter filter = (CDeviceFilter)obj;
            if (filter.mVendorId != this.mVendorId || filter.mProductId != this.mProductId || filter.mClass != this.mClass || filter.mSubclass != this.mSubclass || filter.mProtocol != this.mProtocol)
                return false;
            if ((filter.mManufacturerName != null && this.mManufacturerName == null) || (filter.mManufacturerName == null && this.mManufacturerName != null) || (filter.mProductName != null && this.mProductName == null) || (filter.mProductName == null && this.mProductName != null) || (filter.mSerialNumber != null && this.mSerialNumber == null) || (filter.mSerialNumber == null && this.mSerialNumber != null))
                return false;
            if ((filter.mManufacturerName != null && this.mManufacturerName != null &&
                    !this.mManufacturerName.equals(filter.mManufacturerName)) || (filter.mProductName != null && this.mProductName != null &&

                    !this.mProductName.equals(filter.mProductName)) || (filter.mSerialNumber != null && this.mSerialNumber != null &&

                    !this.mSerialNumber.equals(filter.mSerialNumber)))
                return false;
            return true;
        }
        if (obj instanceof UsbDevice) {
            UsbDevice device = (UsbDevice)obj;
            if (device.getVendorId() != this.mVendorId || device
                    .getProductId() != this.mProductId || device
                    .getDeviceClass() != this.mClass || device
                    .getDeviceSubclass() != this.mSubclass || device
                    .getDeviceProtocol() != this.mProtocol)
                return false;
            return true;
        }
        return false;
    }

    public int hashCode() {
        return (this.mVendorId << 16 | this.mProductId) ^ (this.mClass << 16 | this.mSubclass << 8 | this.mProtocol);
    }

    public String toString() {
        return "DeviceFilter[mVendorId=" + this.mVendorId + ",mProductId=" + this.mProductId + ",mClass=" + this.mClass + ",mSubclass=" + this.mSubclass + ",mProtocol=" + this.mProtocol + ",mManufacturerName=" + this.mManufacturerName + ",mProductName=" + this.mProductName + ",mSerialNumber=" + this.mSerialNumber + "]";
    }
}
